/**
 * 
 */
package com.google.code.netz.wfe.events;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.netz.wfe.api.IManaged;

/**
 * Manages Events, Listeners and Event dispatches to appropriate listeners.
 * <p>
 * This is similar to a notification bus.
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id: EventManager.java 258 2008-11-29 06:23:51Z rahul.thakur.xdev $
 * @see <code><a href='http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/6635205f5ce8208a/2cffc8ca1d81471f?lnk=gst&q=Event+manager#2cffc8ca1d81471'>
 *      Event Manager</a></code>
 */
public class EventManager {

	/**
	 * Commons Logging
	 */
	private static Log log = LogFactory.getLog(EventManager.class);

	/**
	 * Singleton {@link EventManager} instance.
	 */
	private static final EventManager instance = new EventManager();

	/**
	 * Collection of {@link ITypedEventListener}s registered for different
	 * <em>types</em> of {@link TypedEvent}s.
	 */
	private static final Map<IEventType, List<ITypedEventListener<? extends TypedEvent>>> listeners = new HashMap<IEventType, List<ITypedEventListener<? extends TypedEvent>>>();

	/**
	 * Returns a singleton instance of {@link EventManager}.
	 * 
	 * @return
	 */
	public static EventManager getInstance() {
		return instance;
	}

	/**
	 * Adds a Registered Listener.
	 * <p>
	 * TODO: Can we make this method definition more type safe such that we are
	 * only allowed to add Listeners which listen/handle a corresponding event
	 * type.
	 * 
	 * @param <T>
	 *            extension of {@link ITypedEventListener}
	 * @param type
	 *            type of {@link TypedEvent}.
	 * @param listener
	 *            {@link ITypedEventListener} to add.
	 */
	@SuppressWarnings("unchecked")
	public <T extends ITypedEventListener<? extends TypedEvent>> void addEventListener(IEventType type, T listener) {
		List<ITypedEventListener<? extends TypedEvent>> typedListeners = null;
		if (!listeners.containsKey(type)) {
			listeners.put(type, new ArrayList<ITypedEventListener<? extends TypedEvent>>());

			System.out.println("Added listener for event type: " + type.asString());
		}
		typedListeners = listeners.get(type);
		typedListeners.add((ITypedEventListener<TypedEvent>) listener);
	}

	/**
	 * Removes a Registered listener.
	 * 
	 * @param <T>
	 *            extension of {@link ITypedEventListener}
	 * @param type
	 *            type of {@link TypedEvent}.
	 * @param listener
	 *            {@link ITypedEventListener} to remove.
	 */
	public <T extends ITypedEventListener<? extends TypedEvent>> void removeEventListener(IEventType type, T listener) {
		List<ITypedEventListener<? extends TypedEvent>> registeredListeners = listeners.get(type);
		if (null != registeredListeners)
			registeredListeners.remove(listener);
	}

	/**
	 * Dispatches Event notification to {@link ITypedEventListener}s registered
	 * for that particular type of Event.
	 * 
	 * @param <E>
	 *            extension of {@link TypedEvent}.
	 * @param event
	 *            event to handle
	 */
	@SuppressWarnings("unchecked")
	public <E extends TypedEvent> void dispatchEvent(final E event) {
		if (listeners.containsKey(event.getType())) {
			List<ITypedEventListener<? extends TypedEvent>> list = listeners.get(event.getType());
			System.out.println("Dispatching event to the Event handler");
			for (ITypedEventListener tl : list) {
				tl.onEvent(event);
			}
		}
	}
}
